/*
 * Copyright (C) 2011 The Android Open Source Project
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package hellfire.system.graphics;

import hellfire.system.engine.Main;

import java.nio.ByteBuffer;
import java.nio.ByteOrder;
import java.nio.FloatBuffer;
import java.nio.ShortBuffer;

import javax.microedition.khronos.opengles.GL10;

import android.opengl.GLES20;
import android.opengl.Matrix;
import android.util.Log;

public class GLString implements GLDrawable
{
	//Shader program ID
	private final int program;
    
	//font resource ID
    private final int resourceId;
    
    //Position & Size properties
    private GLVec2 pos, dim;
    
    //text color
    private float[] textColor; //not GLColor to avoid 'new' when feeding shader
    
    //background color
    private float[] bgColor; //not GLColor to avoid 'new' when feeding shader
    
    //Cache for handles
    private GLCache cache = new GLCache();
    
    private String str;
    
    //do we need to rebuild the texture
    private boolean needBuildTexture;
    
    //handle to the string texture
    private int stringTex[];
    
    public GLString(int fontResourceId, String str, GLVec2 pos, GLVec2 dim) 
    {
    	program = Main.UI.renderer.getShaderProgram(Shaders.pString);
    	this.pos = pos;
    	this.dim = dim;
    	this.resourceId = fontResourceId;
    	this.textColor = GLColor.WHITE.asFloatArray();
    	this.bgColor = new float[]{0,0,0,0};
    	this.str = str;
    	stringTex = new int[1];
    	stringTex[0] = -1;
    	this.needBuildTexture = true;
    }
    
    public GLString(int fontResourceId, String str, GLVec2 startPos, double font)
    {
    	program = Main.UI.renderer.getShaderProgram(Shaders.pString);
    	this.pos = startPos.add(0.5f*(float)font*str.length(), 0);
    	this.dim = new GLVec2(0.5*font*str.length(),font);
    	this.resourceId = fontResourceId;
    	this.textColor = GLColor.WHITE.asFloatArray();
    	this.bgColor = new float[]{0,0,0,0};
    	this.str = str;
    	stringTex = new int[1];
    	stringTex[0] = -1;
    	this.needBuildTexture = true;
    }
    
   
    public void setTextColor(GLColor c)
    {
    	if(c == null) 
    		this.textColor = GLColor.WHITE.asFloatArray();
    	else this.textColor = c.asFloatArray();
    }
   
    
    public void setBackgroundColor(GLColor c)
    {
    	if(c == null) 
    		this.bgColor = new float[]{0,0,0,0};
    	else this.bgColor = c.asFloatArray();
    }
    
    /**
     * @return A new GLColor object. It is unique.
     */
    public GLColor getMaskColor()
    {
    	return new GLColor(this.textColor);
    }
    
    public void setString(String str, boolean alsoResize)
    {
    	if(alsoResize && str.length() > 0)
    	{
    		float charSize = dim.x() / this.str.length();
    		this.dim = new GLVec2(charSize * str.length(), dim.y());
    		this.pos = new GLVec2(this.pos.x() + charSize * (str.length() - this.str.length()), this.pos.y());
    	}
    	this.str = str;
    	needBuildTexture = true;
    }
    
    public String getString()
    {
    	return this.str;
    }
    
    public void draw(float[] matrix) 
    {
    	if(needBuildTexture) 
    	{
    		buildTexture();
    		needBuildTexture = false;
    	}
    	
    	int mPositionHandle;
    	int mTexelHandle;
    	int mColorHandle;
    	int mBgColorHandle;
        int mMVPMatrixHandle;
        int mFontTextureHandle;
        int mFontTexture;
        int mStringTexture;
        int mStringTextureHandle;
        int mStrLengthHandle;
        
        Main.UI.renderer.pushMatrix(matrix);
        Matrix.translateM(matrix, 0, pos.x(), pos.y(), 0);
        Matrix.scaleM(matrix, 0, matrix, 0, dim.x(), dim.y(), 1);
    	GLES20.glUseProgram(program);
    	
    	if((mFontTexture = cache.get(0)) < 0)
        	mFontTexture = cache.set(0, Main.UI.renderer.ResourceIdToTexture(resourceId));
    	
    	mStringTexture = stringTex[0];
    	
        if((mPositionHandle = cache.get(1)) < 0)
        	mPositionHandle = cache.set(1, GLES20.glGetAttribLocation(program, "aPosition"));
        
        if((mColorHandle = cache.get(2)) < 0)
        	mColorHandle = cache.set(2, GLES20.glGetUniformLocation(program, "uColor"));
        
        if((mTexelHandle = cache.get(3)) < 0)
        	mTexelHandle = cache.set(3, GLES20.glGetAttribLocation(program, "aTexCoord"));
        
        if((mFontTextureHandle = cache.get(4)) < 0)
        	mFontTextureHandle = cache.set(4, GLES20.glGetUniformLocation(program, "uFontTexture"));
        
        if((mStringTextureHandle = cache.get(5)) < 0)
        	mStringTextureHandle = cache.set(5, GLES20.glGetUniformLocation(program, "uString"));
        
        if((mMVPMatrixHandle = cache.get(6)) < 0) 
        	mMVPMatrixHandle = cache.set(6, GLES20.glGetUniformLocation(program, "uMVPMatrix"));
        
        if((mStrLengthHandle = cache.get(7)) < 0) 
        	mStrLengthHandle = cache.set(7, GLES20.glGetUniformLocation(program, "uLength"));
        
        if((mBgColorHandle = cache.get(8)) < 0) 
        	mBgColorHandle = cache.set(8, GLES20.glGetUniformLocation(program, "uBackground"));
        
        // Enable a handle to the triangle vertices
        GLES20.glEnableVertexAttribArray(mPositionHandle);

        // Prepare the triangle coordinate data
        GLES20.glVertexAttribPointer(
                mPositionHandle, 3,
                GLES20.GL_FLOAT, false,
                12, GLRect.vertexBuffer);
        
        GLES20.glEnableVertexAttribArray(mTexelHandle);
        GLES20.glVertexAttribPointer(
        		mTexelHandle, 2,
                GLES20.GL_FLOAT, false,
                0, GLImage.texCoordBuffer);
        
        GLES20.glUniform4fv(mColorHandle, 1, textColor, 0);
        
        GLES20.glActiveTexture(GLES20.GL_TEXTURE0);
        GLES20.glBindTexture(GLES20.GL_TEXTURE_2D, mFontTexture);
        GLES20.glUniform1i(mFontTextureHandle, 0);
        
        GLES20.glActiveTexture(GLES20.GL_TEXTURE1);
        GLES20.glBindTexture(GLES20.GL_TEXTURE_2D, mStringTexture);
        GLES20.glUniform1i(mStringTextureHandle, 1);
        
        GLES20.glUniform1f(mStrLengthHandle, str.length());
        
        GLES20.glUniform4fv(mColorHandle, 1, textColor, 0);
        
        GLES20.glUniform4fv(mBgColorHandle, 1, bgColor, 0);
     
        // Apply the projection and view transformation
        GLES20.glUniformMatrix4fv(mMVPMatrixHandle, 1, false, matrix, 0);
        //GLRenderer.checkGLError("glUniformMatrix4fv");

        // Draw the square
        GLES20.glDrawElements(
                GLES20.GL_TRIANGLES, GLRect.drawOrder.length,
                GLES20.GL_UNSIGNED_SHORT, GLRect.drawListBuffer);

        // Disable vertex array
        GLES20.glDisableVertexAttribArray(mPositionHandle);
        GLES20.glDisableVertexAttribArray(mTexelHandle);
    	
        Main.UI.renderer.popMatrix(matrix);
    }

	private void buildTexture() 
	{
		if(stringTex[0] != -1)
		{
			GLES20.glDeleteTextures(1, stringTex, 0);
			stringTex[0] = -1;
		}
		GLES20.glGenTextures(1, stringTex, 0);
		GLES20.glBindTexture(GLES20.GL_TEXTURE_2D, stringTex[0]);
		ByteBuffer strBuffer = ByteBuffer.wrap(str.getBytes());
		GLES20.glTexParameteri(GLES20.GL_TEXTURE_2D, GLES20.GL_TEXTURE_MIN_FILTER, GLES20.GL_NEAREST); 
		GLES20.glTexParameteri(GLES20.GL_TEXTURE_2D, GLES20.GL_TEXTURE_MAG_FILTER, GLES20.GL_NEAREST);
		GLES20.glTexImage2D(GLES20.GL_TEXTURE_2D, 0, GLES20.GL_ALPHA, str.length(), 1, 0, GLES20.GL_ALPHA, GLES20.GL_UNSIGNED_BYTE, strBuffer);
	}

	public GLVec2 getPos() 
	{
		return pos;
	}
	
	public void setStartPos(GLVec2 pos)
	{
		setPos(pos.add(dim.x(), 0));
		
	}
	
	public void setPos(GLVec2 pos) 
	{
		this.pos = pos;
	}
    

}